package com.zyh.scs.admin.service.jd;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zyh.scs.admin.api.constant.UserStateEnum;
import com.zyh.scs.admin.api.dto.jd.PayConfirmParam;
import com.zyh.scs.admin.api.dto.jd.RefundOrder;
import com.zyh.scs.admin.api.dto.jd.ReverseParam;
import com.zyh.scs.admin.api.entity.SysUser;
import com.zyh.scs.admin.api.entity.UserDeduction;
import com.zyh.scs.admin.api.entity.UserOrderInfo;
import com.zyh.scs.admin.api.entity.UserTransaction;
import com.zyh.scs.admin.api.utils.UserTypeToNameUtil;
import com.zyh.scs.admin.base.jd.JdResultParam;
import com.zyh.scs.admin.base.jd.QueryRefundParam;
import com.zyh.scs.admin.base.jd.QueryResponse;
import com.zyh.scs.admin.mapper.UserDeductionMapper;
import com.zyh.scs.admin.mapper.UserOrderInfoMapper;
import com.zyh.scs.admin.mapper.UserTransactionMapper;
import com.zyh.scs.admin.service.SysUserService;
import com.zyh.scs.admin.service.UserOrderInfoService;
import com.zyh.scs.admin.utils.jd.HttpUtils;
import com.zyh.scs.admin.utils.jd.PayConfirmParamUtil;
import com.zyh.scs.admin.utils.jd.QueryRefundParamUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

;

/**
 * @author hwh
 * @ClassName: 对接京东实现类$
 * @Description: $
 * @Date $
 * @Version 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class JDServiceImpl implements JDService {

    private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
    private final UserOrderInfoMapper userOrderInfoMapper;
    private final UserOrderInfoService userOrderInfoService;
    private final SysUserService sysUserService;
    private final UserDeductionMapper userDeductionMapper;
    private final UserTransactionMapper userTransactionMapper;
    private final RedissonClient redissonClient;
    /**
     * 京东BPIIN
     */
    @Value("${biz.jd.bpin}")
    public String BPIN;
    /**
     * 京东accessKey
     */
    @Value("${biz.jd.accessKey}")
    public String ACCESS_KEY;
    /**
     * 京东私钥
     */
    @Value("${biz.jd.rsaSignPrivateKey}")
    public String RSA_SIGN_PRIVATE_KEY;
    /**
     * 京东公钥
     */
    @Value("${biz.jd.rsaEncryptPublicKey}")
    public String RSA_ENCRYPT_PUBLIC_KEY;
    /**
     * 查询退款订单
     */
    @Value("${biz.jd.refundOrderUrl}")
    public String REFUND_ORDER_URL;
    /**
     * 订单状态同步
     */
    @Value("${biz.jd.confirmOrderUrl}")
    public String JD_CONFIRM_URL;
    /**
     * 逆向退款对接
     */
    @Value("${biz.jd.reverseOrderUrl}")
    public String JD_REVERSE_URL;

    private final String USER_TASK_KEY = "USER_TASK_KEY";


    /**
     * 京东 订单确认支付接口
     *
     * @param payConfirmParam
     * @return
     */
    private Boolean payConfirmToJd(PayConfirmParam payConfirmParam) {

        //JD_confirm_URL
        Map map = new HashMap<>(2);
        map = JSONObject.parseObject(JSONObject.toJSONString(payConfirmParam), Map.class);
        String jdRes = HttpUtils.sendFormPost(JD_CONFIRM_URL, null, map);
        log.info("调用京东确认接口返回报文：{}", jdRes);
        JdResultParam JdResultParam = JSONObject.parseObject(jdRes, JdResultParam.class);
        if ("0003".equals(JdResultParam.getResultCode())   //确认成功
                || "3103".equals(JdResultParam.getResultCode())) {   //重复确认
            return true;
        } else {
            return false;
        }
    }

    /**
     * 京东 订单确认支付接口
     *
     * @return
     */
    @Override
    public boolean syncOrderStatusToJd() {
        //获取当前订单未同步和订单完成的
        List<UserOrderInfo> orderInfoList = userOrderInfoMapper.selectList(Wrappers.<UserOrderInfo>lambdaQuery().eq(UserOrderInfo::getOrderStatus, "8").eq(UserOrderInfo::getWhetSync, "1"));
        if (orderInfoList == null || orderInfoList.isEmpty()) {
            return false;
        }
        for (UserOrderInfo orderInfo : orderInfoList) {
            try {
                //同步订单状态给到京东  测试发现，accessKey
                Boolean res = payConfirmToJd(PayConfirmParamUtil.getPayConfirmParam(RSA_SIGN_PRIVATE_KEY, RSA_ENCRYPT_PUBLIC_KEY, ACCESS_KEY, orderInfo.getOutOrderNo()));
                if (res) {
                    //更新订单状态
                    UserOrderInfo orderInfoUpdate = new UserOrderInfo();
                    orderInfoUpdate.setId(orderInfo.getId());
                    orderInfoUpdate.setWhetSync("2"); //已同步
                    orderInfoUpdate.setUpdateTime(LocalDateTime.now());
                    userOrderInfoMapper.updateById(orderInfoUpdate);
                }
            } catch (Exception e) {
                log.error("同步订单到京东出错:", e);
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public void syncOrderReverseToJd() {

    }

    /**
     * 查询退款订单
     */
    @Override
    public boolean queryRefundOrder() {
        RLock lock = redissonClient.getLock(USER_TASK_KEY + "queryRefundOrder");

        try {
            // 尝试加锁，若获取不到锁则返回
            boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS);
            if (!isLocked) {
                log.warn("无法获取任务锁，任务正在执行中");
                return false;
            }

            String startDate = LocalDate.now().minusDays(3).atStartOfDay().format(dateTimeFormatter);
            String endDate = LocalDateTime.now().format(dateTimeFormatter);

            int i = 1;
            while (true) {
                List<RefundOrder> res = queryOrderToJD(QueryRefundParamUtil.queryRefundOrderParam(RSA_SIGN_PRIVATE_KEY, RSA_ENCRYPT_PUBLIC_KEY, ACCESS_KEY, startDate, endDate, String.valueOf(100), String.valueOf(i)));

                if (res == null || res.isEmpty()) {
                    log.info("调用京东确认接口返回数据为空");
                    break;
                }
                for (RefundOrder refundOrder : res) {
                    // 异步处理订单，减少事务操作时间
                    refundOrder(refundOrder);
                }
                i++;
            }

            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("查询退款订单失败：", e);
            throw new RuntimeException("查询退款订单失败：", e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 处理京东退款订单
     */
    private void refundOrder(final RefundOrder refundOrder) {

        final String refundOrderId = refundOrder.getId();
        if (StrUtil.isBlank(refundOrderId)){
            return;
        }

        try {
            //查询退款流水
            //存在，return
            if (userDeductionMapper.exists(Wrappers.<UserDeduction>query().lambda().eq(UserDeduction::getOutRefundId,refundOrderId))) {
                return;
            }

            //查询订单
            UserOrderInfo orderInfo = userOrderInfoMapper.selectOne(Wrappers.<UserOrderInfo>lambdaQuery()
                    .eq(UserOrderInfo::getOutOrderNo, refundOrder.getParentOrderId().toString())
                    .last(" limit 1"));
            if (orderInfo == null) {
                log.info("此次退货已处理，京东订单号为：{},退货id为:{}", refundOrder.getParentOrderId(), refundOrderId);
                return;
            }
            if (orderInfo.getOrderStatus() != 8 && orderInfo.getOrderStatus() != 12){
                log.info("订单状态有误，不能退：{}", orderInfo);
                return;
            }

            SysUser sysUser = sysUserService.lambdaQuery().eq(SysUser::getGuid, orderInfo.getGuid()).one();
            if (null == sysUser || UserStateEnum.LOCK.getCode().equals(sysUser.getLockFlag())) {
                log.info("用户不存在或被冻结:getGuid:{}", orderInfo.getGuid());
                return;
            }
            //判断部分退款
            //计算
            BigDecimal refundAmount = refundOrder.getReturnBp();

            // 判断退款金额是否超出订单剩余可退总金额
            if (refundAmount.compareTo(orderInfo.getAmount().subtract(orderInfo.getAllRefundAmount())) > 0) {
                log.info("退款金额超出订单可退金额，退款失败,京东订单号:{},退货id:{}", orderInfo.getOutOrderNo(), refundOrderId);
                return;
            }

            //订单中现金部分
            BigDecimal cashPartOfOrder = orderInfo.getCashBalance();
            //累计现金已退款
            BigDecimal refundedCashOfOrder = orderInfo.getCashRefundAmount();
            // 剩余可退现金
            BigDecimal refundedCashCan = cashPartOfOrder.subtract(refundedCashOfOrder);
            //退现金
            BigDecimal userRefundCash = refundAmount.compareTo(refundedCashCan) > 0 ? refundedCashCan : refundAmount;
            //退餐补
            BigDecimal userRefundBalance = refundAmount.subtract(userRefundCash);

            if (userRefundCash.compareTo(BigDecimal.ZERO) > 0 && "2".equals(sysUser.getCashState())){
                log.info("账户现金账户冻结,无法退款，guid:{}",sysUser.getGuid());
                return;
            }
            if (userRefundBalance.compareTo(BigDecimal.ZERO) > 0 && "2".equals(sysUser.getCardState())){
                log.info("账户餐卡账户冻结,无法退款，guid:{}",sysUser.getGuid());
                return;
            }
            //判断是否全部退款
            Integer status = orderInfo.getAllRefundAmount().add(refundAmount).compareTo(orderInfo.getAmount()) == 0 ? 10 : 12;
            //更新订单
            final LocalDateTime now = LocalDateTime.now();
            userOrderInfoService.lambdaUpdate()
                    .eq(UserOrderInfo::getId,orderInfo.getId())
                    .setIncrBy(UserOrderInfo::getAllRefundAmount,refundAmount)
                    .setIncrBy(userRefundBalance.compareTo(BigDecimal.ZERO) > 0, UserOrderInfo::getRefundAmount, userRefundBalance)
                    .setIncrBy(userRefundCash.compareTo(BigDecimal.ZERO) > 0, UserOrderInfo::getCashRefundAmount, userRefundCash)
                    .set(UserOrderInfo::getOrderStatus, status)
                    .set(UserOrderInfo::getUpdateTime, now)
                    .update();
            //金额回滚
            sysUserService.lambdaUpdate().eq(SysUser::getUserId, sysUser.getUserId())
                    .setIncrBy(userRefundBalance.compareTo(BigDecimal.ZERO) > 0, SysUser::getCardAccount, userRefundBalance)
                    .setIncrBy(userRefundCash.compareTo(BigDecimal.ZERO) > 0, SysUser::getCashAccount, userRefundCash)
                    .set(SysUser::getUpdateBy, sysUser.getUserId())
                    .set(SysUser::getUpdateTime, now)
                    .update();
            //修改用户金额
            sysUser.setCardAccount(sysUser.getCardAccount().add(userRefundBalance));
            //新增流水 退款和交易  现金和餐卡
            //先餐补
            final String notes = "京东-锦礼退货";
            if (userRefundBalance.compareTo(BigDecimal.ZERO) > 0) {
                String rechargeStream = UserTypeToNameUtil.setRechargeStream();
                UserDeduction userDeductionCard = new UserDeduction();
                //userDeductionCard.setDeductionId();
                userDeductionCard.setUserId(sysUser.getUserId());
                userDeductionCard.setUsername(sysUser.getUsername());
                userDeductionCard.setUserJobNumber(sysUser.getUserJobNumber());
                userDeductionCard.setPhone(sysUser.getPhone());
                userDeductionCard.setUserType(sysUser.getUserType());
                userDeductionCard.setUserMealsNumber(sysUser.getUserMealsNumber());
                userDeductionCard.setAccountType("1");
                userDeductionCard.setPaymentChannel("2");
                userDeductionCard.setTransactionTime(now);
                userDeductionCard.setRechargeStream(rechargeStream);
                userDeductionCard.setThirdRechargeStream(refundOrder.getRefundOrderId());
                userDeductionCard.setNotes(notes);
                userDeductionCard.setCreateTime(now);
                userDeductionCard.setCreateBy(sysUser.getGuid());
                userDeductionCard.setDelFlag("0");
                userDeductionCard.setDeductionNumber(userRefundBalance);
                userDeductionCard.setCashAccount(sysUser.getCashAccount());
                userDeductionCard.setCardAccount(sysUser.getCardAccount());
                userDeductionCard.setOrderId(orderInfo.getOrderId());
                userDeductionCard.setOutOrderNo(orderInfo.getOutOrderNo());
                userDeductionCard.setGuid(sysUser.getGuid());
                userDeductionCard.setOutRefundId(refundOrderId);

                UserTransaction userTransactionCard = new UserTransaction();
                //userTransactionCard.setTransactionId();
                userTransactionCard.setRechargeStream(rechargeStream);
                userTransactionCard.setThirdRechargeStream(refundOrder.getRefundOrderId());
                userTransactionCard.setUserId(sysUser.getUserId());
                userTransactionCard.setUsername(sysUser.getUsername());
                userTransactionCard.setUserJobNumber(sysUser.getUserJobNumber());
                userTransactionCard.setPhone(sysUser.getPhone());

                userTransactionCard.setTransactionNumber(userRefundBalance);
                userTransactionCard.setCashAccount(sysUser.getCashAccount());
                userTransactionCard.setCardAccount(sysUser.getCardAccount());
                userTransactionCard.setAccountType("1");
                userTransactionCard.setMerchantNumber(orderInfo.getMerchantId());
                userTransactionCard.setTransactionType("5");
                userTransactionCard.setCreateTime(now);
                userTransactionCard.setDelFlag("0");
                userTransactionCard.setTransactionTime(now);
                userTransactionCard.setNotes(notes);
                userTransactionCard.setUserType(sysUser.getUserType());
                userTransactionCard.setOrderId(orderInfo.getOrderId());
                userTransactionCard.setOutOrderNo(orderInfo.getOutOrderNo());
                userTransactionCard.setOperator(sysUser.getGuid());
                userTransactionCard.setTradeType(1);
                userTransactionCard.setGuid(sysUser.getGuid());
                userDeductionMapper.insert(userDeductionCard);
                userTransactionCard.setUserMealsNumber(sysUser.getUserMealsNumber());
                userTransactionMapper.insert(userTransactionCard);
            }
            //修改用户金额
            sysUser.setCashAccount(sysUser.getCashAccount().add(userRefundCash));
            //现金
            if (userRefundCash.compareTo(BigDecimal.ZERO) > 0) {
                String rechargeStream = UserTypeToNameUtil.setRechargeStream();
                UserDeduction userDeductionCash = new UserDeduction();
                //userDeductionCash.setDeductionId();
                userDeductionCash.setUserId(sysUser.getUserId());
                userDeductionCash.setUsername(sysUser.getUsername());
                userDeductionCash.setUserJobNumber(sysUser.getUserJobNumber());
                userDeductionCash.setPhone(sysUser.getPhone());
                userDeductionCash.setUserType(sysUser.getUserType());
                userDeductionCash.setUserMealsNumber(sysUser.getUserMealsNumber());
                userDeductionCash.setAccountType("2");
                userDeductionCash.setPaymentChannel("2");
                userDeductionCash.setTransactionTime(now);
                userDeductionCash.setRechargeStream(rechargeStream);
                userDeductionCash.setThirdRechargeStream(refundOrder.getRefundOrderId());
                userDeductionCash.setNotes(notes);
                userDeductionCash.setCreateTime(now);
                userDeductionCash.setCreateBy(sysUser.getGuid());
                userDeductionCash.setDelFlag("0");
                userDeductionCash.setDeductionNumber(userRefundCash);
                userDeductionCash.setCashAccount(sysUser.getCashAccount());
                userDeductionCash.setCardAccount(sysUser.getCardAccount());
                userDeductionCash.setOrderId(orderInfo.getOrderId());
                userDeductionCash.setOutOrderNo(orderInfo.getOutOrderNo());
                userDeductionCash.setGuid(sysUser.getGuid());
                userDeductionCash.setOutRefundId(refundOrderId);

                UserTransaction userTransactionCash = new UserTransaction();
                //userTransactionCash.setTransactionId();
                userTransactionCash.setRechargeStream(rechargeStream);
                userTransactionCash.setThirdRechargeStream(refundOrder.getRefundOrderId());
                userTransactionCash.setUserId(sysUser.getUserId());
                userTransactionCash.setUsername(sysUser.getUsername());
                userTransactionCash.setUserJobNumber(sysUser.getUserJobNumber());
                userTransactionCash.setPhone(sysUser.getPhone());
                userTransactionCash.setTransactionNumber(userRefundCash);
                userTransactionCash.setCashAccount(sysUser.getCashAccount());
                userTransactionCash.setCardAccount(sysUser.getCardAccount());
                userTransactionCash.setAccountType("2");
                userTransactionCash.setMerchantNumber(orderInfo.getMerchantId());
                userTransactionCash.setTransactionType("5");
                userTransactionCash.setCreateTime(now);
                userTransactionCash.setDelFlag("0");
                userTransactionCash.setTransactionTime(now);
                userTransactionCash.setNotes(notes);
                userTransactionCash.setUserType(sysUser.getUserType());
                userTransactionCash.setOrderId(orderInfo.getOrderId());
                userTransactionCash.setOutOrderNo(orderInfo.getOutOrderNo());
                userTransactionCash.setOperator(sysUser.getGuid());
                userTransactionCash.setTradeType(1);
                userTransactionCash.setGuid(sysUser.getGuid());
                userDeductionMapper.insert(userDeductionCash);
                userTransactionCash.setUserMealsNumber(sysUser.getUserMealsNumber());
                userTransactionMapper.insert(userTransactionCash);
            }

            log.info("此次退货已处理，设置redis锁，锁30天,京东订单号:{},退货id:{}", orderInfo.getOutOrderNo(), refundOrderId);
        } catch (Exception e) {
            log.error("退款失败，退款订单号:{}", refundOrderId, e);
        }

    }

    /**
     * 京东 逆向退款接口
     *
     * @param reverseParam
     * @return
     */
    private Boolean payReverseToJd(ReverseParam reverseParam) {

        //JD_REVERSE_URL
        Map map = new HashMap<>(2);
        map = JSONObject.parseObject(JSONObject.toJSONString(reverseParam), Map.class);
        String jdRes = HttpUtils.sendFormPost(JD_REVERSE_URL, null, map);
        log.info("调用京东逆向退款接口返回报文：{}", jdRes);
        JdResultParam JdResultParam = JSONObject.parseObject(jdRes, JdResultParam.class);
        //确认成功
        if ("0003".equals(JdResultParam.getResultCode())
                //重复确认
                || "3103".equals(JdResultParam.getResultCode())) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 查询退款订单
     *
     * @param queryRefundParam
     * @return
     */
    private List<RefundOrder> queryOrderToJD(QueryRefundParam queryRefundParam) {

        //JD_confirm_URL
        Map map = new HashMap<>(2);
        map = JSONObject.parseObject(JSONObject.toJSONString(queryRefundParam), Map.class);
        String jdRes = HttpUtils.sendFormPost(REFUND_ORDER_URL, null, map);
        log.info("调用京东查询退款订单接口返回报文：{}", jdRes);
        QueryResponse queryResponse = JSONObject.parseObject(jdRes, QueryResponse.class);
        //查询成功
        if (queryResponse.getSuccess()) {
            return queryResponse.getResult().getData();
        } else {
            return null;
        }
    }
}
